package edu.spbsu.nonsm00th.model;

import edu.spbsu.nonsm00th.util.Precondition;

/**
 * 3-dimension plane representation class.
 * <p>
 * The plane equation is:
 * <pre>
 * a*x + b*y + c*z + d = 0
 * </pre>
 * 
 * @author mukhadin
 */
public class Plane3D {

    /** The <code>a</code> constant */
    private double a;

    /** The <code>b</code> constant */
    private double b;

    /** The <code>c</code> constant */
    private double c;

    /** The <code>d</code> constant */
    private double d;

    /**
     * The default constructor.
     */
    public Plane3D(double a, double b, double c, double d) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
    }

    public Plane3D(Plane p) {
        this(p.getA(), p.getB(), p.getC());
    }

    public Plane3D(Line l, Point p) {
        this(l.getA(), l.getB(), p);
    }

    public Plane3D(Point a, Point b, Point c) {
        Precondition.argsNotNull(a, b, c);
        Precondition.check(3 == a.getDimension() && 3 == b.getDimension() && 3 == c.getDimension(),
                "Dimension of Points a, b and c should be 3.");
        Vector a0 = new Vector(a, new Point(3));
        Vector ab = new Vector(a, b);
        Vector ac = new Vector(a, c);
        Vector cross = Vector.crossProduct(ab, ac);
        this.a = cross.getX();
        this.b = cross.getY();
        this.c = cross.getZ();
        this.d = Vector.dotProduct(a0, cross);
    }

    /**
     * Calculates distance between current plane and some <code>Point</code>.
     *
     * @param p the point
     * @return the distance
     */
    public double getDistance(Point p) {
        return getDeviation(p) / getNorm();
    }

    /**
     * Calculates the norm.
     *
     * @return the norm
     */
    private double getNorm() {
        return a * a + b* b + c * c;
    }

    /**
     * Calculates deviation for point.
     *
     * @param p the point
     * @return the deviation
     */
    private double getDeviation(Point p) {
        return a * p.getX() + b * p.getY() + c * p.getZ() + d;
    }

    public double getA() {
        return a;
    }

    public void setA(double a) {
        this.a = a;
    }

    public double getB() {
        return b;
    }

    public void setB(double b) {
        this.b = b;
    }

    public double getC() {
        return c;
    }

    public void setC(double c) {
        this.c = c;
    }

    public double getD() {
        return d;
    }

    public void setD(double d) {
        this.d = d;
    }

}
